package de.dhbw.vs.permachat.server.persistence;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

/*import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;*/
import javax.persistence.EntityManager;
import javax.persistence.Query;

import de.dhbw.vs.permachat.server.persistence.entities.ChatMessage;
import de.dhbw.vs.permachat.server.persistence.entities.ChatRoom;
import de.dhbw.vs.permachat.server.persistence.entities.ChatUser;

/**
 * Zugriffs-Klasse für persistente Chat Entitäten
 * @author flo
 *
 */
/*@Named
@RequestScoped*/
public class ChatDao {
	/*@Inject*/
	EntityManager em;
	
	public ChatDao(){
		this.em = EMF.get().createEntityManager();
	}
	
	/**
	 * Liefert einen bestehenden ChatRoom zurück.
	 * Existiert der ChatRoom nicht, wird null zurückgegeben.
	 * @param pName Name des ChatRooms
	 * @return ChatRoom
	 */
	@SuppressWarnings("unchecked")
	public ChatRoom getChatRoom(String pName){
		Query query = em.createQuery("select r from ChatRoom r where r.name = :name");
		query.setParameter("name", pName);
		List<ChatRoom> chatRooms = query.getResultList();
		if(chatRooms.isEmpty()){
			return null;
		} else{
			return chatRooms.iterator().next();
		}
	}

	/**
	 * Erzeugt oder aktualisiert einen ChatRoom.
	 * @param pName Name des ChatRooms
	 * @return ChatRoom
	 */
	public ChatRoom createOrUpdateChatRoom(String pName, double pLatitude, double pLongitude) {
		if("nearest".equalsIgnoreCase(pName)){
			throw new IllegalArgumentException("ChatRoom cannot be named 'nearest' as this is a reserved keyword.");
		}
		
		ChatRoom chatRoom = this.getChatRoom(pName);
		
		if(chatRoom == null){
			chatRoom = new ChatRoom();
			chatRoom.setName(pName);
		}
		
		chatRoom.setLatitude(pLatitude);
		chatRoom.setLongitude(pLongitude);
		
		//em.getTransaction().begin();
		this.em.persist(chatRoom);
		//em.getTransaction().commit();
		
		
		return chatRoom;
	}
	
	/**
	 * Gibt eine Liste aller ChatRooms zurück.
	 * @return Liste von ChatRooms
	 */
	@SuppressWarnings("unchecked")
	public List<ChatRoom> listChatRooms(){
		Query query = em.createQuery("select r from ChatRoom r order by r.name");
		List<ChatRoom> chatRooms = query.getResultList();
		
		return chatRooms;
	}
	
	/**
	 * Erzeugt einen neuen bzw. liefert einen bestehenden ChatUser zurück.
	 * @param pName Name des ChatUser
	 * @return ChatUser
	 */
	@SuppressWarnings("unchecked")
	public ChatUser getOrCreateChatUser(String pName){
		Query query = em.createQuery("select u from ChatUser u where u.name = :name");
		query.setParameter("name", pName);
		List<ChatUser> chatUsers = query.getResultList();
		if(chatUsers.isEmpty()){
			return this.createChatUser(pName);
		} else{
			return chatUsers.iterator().next();
		}
	}

	/**
	 * Erzeugt einen neuen ChatUser.
	 * @param pName Name des ChatUsers
	 * @return ChatUser
	 */
	private ChatUser createChatUser(String pName) {
		ChatUser chatUser = new ChatUser();
		chatUser.setName(pName);
		
		//em.getTransaction().begin();
		this.em.persist(chatUser);
		//em.getTransaction().commit();
		return chatUser;
	}
	
	/**
	 * Speichert eine neue ChatMessage.
	 * @param pUser
	 * @param pChatRoom
	 * @param pMessage
	 */
	public void postMessage(String pUser, String pChatRoom, String pMessage){
		ChatUser chatUser = this.getOrCreateChatUser(pUser);
		ChatRoom chatRoom = this.getChatRoom(pChatRoom);
		
		if(chatRoom == null){
			throw new IllegalArgumentException("ChatRoom " + pChatRoom + " does not exist.");
		}
		
		this.postMessage(chatUser, chatRoom, pMessage);
	}
	
	/**
	 * Speichert eine neue ChatMessage.
	 * @param pUser
	 * @param pChatRoom
	 * @param pMessage
	 */
	public void postMessage(ChatUser pUser, ChatRoom pChatRoom, String pMessage){
		ChatMessage chatMessage = new ChatMessage();
		chatMessage.setChatUser(pUser.getName());
		chatMessage.setChatRoom(pChatRoom);
		chatMessage.setTextMessage(pMessage);
		chatMessage.setPostedAt(new Date());
		
		this.postMessage(chatMessage);
	}
	
	/**
	 * Speichert eine neue ChatMessage.
	 * @param pChatMessage
	 */
	public void postMessage(ChatMessage pChatMessage){
		em.getTransaction().begin();
		em.persist(pChatMessage);
		//em.flush();
		//pChatMessage.setMessageId(pChatMessage.getId());
		//em.persist(pChatMessage);

		//Dirty hack as GAE does not support auto-increment...
		//currentTimeMillis as monotonic increasing id is ok
		//for this use case
		//(worst case is messages not shown in GUI)
		pChatMessage.setMessageId(System.currentTimeMillis());
		em.getTransaction().commit();
	}
	
	/**
	 * Gibt eine Liste von ChatMessages zurück.
	 * @param pChatRoom ChatRoom der Messages
	 * @param pCount Anzahl der zurückzugebenden Messages (sortiert von neu nach alt); -1 bedeutet unlimitiert
	 * @param pLastId Messages mit größerer ID werden zurückgegeben; -1 bedeutet von Anfang an
	 * @return Liste von ChatMessages
	 */
	@SuppressWarnings("unchecked")
	public List<ChatMessage> listChatMessages(String pChatRoom, int pCount, long pLastId){
		ChatRoom chatRoom = this.getChatRoom(pChatRoom);
		if(chatRoom == null){
			throw new IllegalArgumentException("ChatRoom " + pChatRoom + " does not exist.");
		}
		
		Query query = em.createQuery("select m from ChatMessage m where m.messageId > :lastId and m.chatRoom = :roomKey order by m.messageId desc");
		query.setParameter("lastId", pLastId);
		query.setParameter("roomKey", chatRoom);
		
		if(pCount > 0){
			query.setMaxResults(pCount);
		}
		
		/*
		 * Liste wird in absteigender Reihenfolge (umgekehrt chronologisch)
		 * abgefragt, um mit setMaxResults die n letzten Messages zu erhalten.
		 * Für den Client sollte die Liste aber wieder in chronologischer
		 * Reihenfolge sein, daher hier der Reverse.
		 * Besser wäre es, wenn das irgendwie im Query direkt umgesetzt werden könnte...
		 */
		List<ChatMessage> resultList = new ArrayList<ChatMessage>(query.getResultList());
		Collections.reverse(resultList);
		
		return resultList;
	}
	
	public void close(){
		this.em.close();
	}
}
